package com.vincent.gdb.test.inject;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javax.annotation.PostConstruct;

import com.google.common.base.Strings;
import com.google.inject.AbstractModule;
import com.google.inject.ProvisionException;
import com.google.inject.TypeLiteral;
import com.google.inject.matcher.Matchers;
import com.google.inject.spi.InjectionListener;
import com.google.inject.spi.TypeEncounter;
import com.google.inject.spi.TypeListener;

/**
 * Calls automatically methods annotated with {@link PostConstruct} after injection.
 * 
 * TODO [nbe] The PreDestroy method is not called yet, there's no hook in guice for that.
 * 
 * @author Nicolas Berthet
 * @since Apr 11, 2012 11:39:10 AM
 */
public class LifecycleModule extends AbstractModule {

	/* (non-Javadoc)
	 * @see com.google.inject.AbstractModule#configure()
	 */
	protected void configure() {
		// Creates a type listener that registers an injection listener on the
		// PostConstruct method
		bindListener(Matchers.any(), new TypeListener() {			
			@Override
			public <I> void hear(TypeLiteral<I> injectableType, TypeEncounter<I> encounter) {
				Class<? super I> type = injectableType.getRawType();
				Method[] methods = type.getDeclaredMethods();
				for (final Method method : methods) {
					final PostConstruct annotation = method.getAnnotation(PostConstruct.class);
					if (annotation != null) {
						encounter.register(new InjectionListener<I>() {
							public void afterInjection(I injectee) {
								try {
									method.invoke(injectee);
								} catch (InvocationTargetException ie) {
									Throwable e = ie.getTargetException();
									throw new ProvisionException(Strings.nullToEmpty(e.getMessage()), e);
								} catch (IllegalAccessException e) {
									throw new ProvisionException(Strings.nullToEmpty(e.getMessage()), e);
								}
							}
						});
					}
				}
			}
		});
	}

}